﻿using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using RegexLib.Web.Models.Data;
using System.Web.Caching;

namespace RegexLib.Web.Models.Web
{
	/// <summary>
	/// 提供IP地址记录功能。
	/// </summary>
	public class RxIPRecorder
	{
		/// <summary>
		/// 屏蔽一个IP。如果IP不存在，则将其添加到屏蔽列表；如果已存在，则更新屏蔽时间和屏蔽原因。
		/// </summary>
		/// <param name="ip">IP地址。</param>
		/// <param name="reason">屏蔽原因。</param>
		public static void BlockIP(string ip, string reason)
		{
			using (RxDataContext db = new RxDataContext())
			{
				var ipr = db.IPBlockeds.FirstOrDefault(b => b.UserIP == ip);
				if (ipr == null)
					db.IPBlockeds.InsertOnSubmit(new IPBlocked { UserIP = ip, BlockedTime = DateTime.Now, Reason = reason });
				else
				{
					ipr.Reason = reason;
					ipr.BlockedTime = DateTime.Now;
				}

				db.SubmitChanges();
			}
		}

		/// <summary>
		/// 记录一个IP操作。
		/// </summary>
		/// <param name="ip">IP地址。</param>
		/// <param name="type">记录类型。</param>
		public static void RecordIP(string ip, RxIPRecordType type)
		{
			var record = new IPRecord { UserIP = ip, RecordType = type, RecordTime = DateTime.Now };

			using (RxDataContext db = new RxDataContext())
			{
				db.IPRecords.InsertOnSubmit(record);
				db.SubmitChanges();
			}
		}

		/// <summary>
		/// 检查IP地址，根据策略决定是暂时屏蔽一个IP还是禁用IP。
		/// </summary>
		/// <param name="ip">IP地址。</param>
		public static void CheckIPPolicy(string ip, RxIPRecordType type)
		{
			if (IsIPBlocked(ip))
				throw new RxIPBlockedException();

			RxIPPolicyCollection policies = RxConfigurationSection.Instance.IPPolicy;
			foreach (RxIPPolicyItem policy in policies)
			{
				if (policy.Type != type)
					continue;

				using (RxDataContext db = new RxDataContext())
				{
					// 检查pause策略
					int records = db.IPRecords.Count(ipr => ipr.RecordTime > DateTime.Now - policy.PausePolicy.Duration);
					if (records > policy.PausePolicy.Records)
						throw new RxIPPausedException();

					// 检查block策略
					records = db.IPRecords.Count(ipr => ipr.RecordTime > DateTime.Now - policy.BlockPolicy.Duration);
					if (records > policy.BlockPolicy.Records)
					{
						string reason = string.Format("违反了[{0}]IP策略。", policy.Type);
						BlockIP(ip, reason);
						throw new RxIPBlockedException();
					}
				}
			}
		}

		const string BlockedIPCacheKey = "RxIPRecorder.BlockedIP";

		/// <summary>
		/// 判断一个IP地址是否已被屏蔽。
		/// </summary>
		/// <param name="ip">待判断的IP地址。</param>
		/// <returns></returns>
		private static bool IsIPBlocked(string ip)
		{
			var ips = GetBlockedIPs();
			return ips.Contains(ip);
		}

		/// <summary>
		/// 获取被屏蔽的IP地址。
		/// </summary>
		/// <returns></returns>
		private static string[] GetBlockedIPs()
		{
			// 1尝试从缓存中读取
			string[] ips = (string[])HttpContext.Current.Cache[BlockedIPCacheKey];

			if (ips != null)
				return ips;

			// 2从数据库中读取
			using (RxDataContext db = new RxDataContext())
			{
				ips = db.IPBlockeds.Select(ipb => ipb.UserIP).ToArray();
			}

			// 3放入缓存
			SqlCacheDependency depend = new SqlCacheDependency("IPBlockedDependency", "IPBlocked");
			HttpContext.Current.Cache.Insert(BlockedIPCacheKey, ips, depend);

			return ips;
		}
	}

	/// <summary>
	/// 表示IP地址已被禁用。
	/// </summary>
	public class RxIPBlockedException : Exception
	{
	}

	/// <summary>
	/// 表示IP地址被暂时屏蔽。
	/// </summary>
	public class RxIPPausedException : Exception
	{
	}
}
